package com.gl.controller.ai;

import com.gl.pojo.Result;
import com.gl.pojo.entity.Meeting;
import com.gl.pojo.vo.AiResponseVo;
import com.gl.service.MeetingService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/ai")
//@CrossOrigin("/*")
public class AIController {

    @Autowired
    private ChatClient chatClient;

    @Autowired
    private InMemoryChatMemory inMemoryChatMemory;

    @Autowired
    private VectorStore vectorStore;

    @Autowired
    private MeetingService meetingService;

    @CrossOrigin(
            origins = "*",
            allowedHeaders = "*",
            exposedHeaders = "Content-Type",
            methods = {RequestMethod.GET}
    )
    @GetMapping(value = "/summary", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> stream(
            @RequestParam(value = "message", defaultValue = "请生成会议总结") String feedback,
            @RequestParam(value = "id") String meetingId
    ) {
        if(feedback == null || feedback.isEmpty()) {
            feedback = "请生成会议总结";
        }

        // 获取会议内容
        // todo yyl 优化升级，在获取元数据的情况下，拼接会议的标题
        Meeting meeting = meetingService.getMeetingInfo(meetingId);
        String meetingContent = meeting.getRecording();

        String meetingMaterials = meetingService.getMeetingMaterials(meetingId, meetingContent);
        String promptWithContext = """
                你现在是MeetingSynth项目中的智能会议总结优化助手，具备10年会议管理经验，擅长将杂乱信息转化为结构化的专业会议纪要，可以进行会议总结或根据用户反馈对已有会议总结进行修改。
                会议标题：
                --------------------
                {meeting_title}
                --------------------
                会议内容：
                --------------------
                {meeting_content}
                --------------------
                会议开始时间：
                --------------------
                {meeting_start_time}
                --------------------
                用户反馈/修改要求：
                --------------------
                {user_feedback}
                --------------------
                请按照以下要求处理：
                1. 优先遵循用户反馈的要求进行修改
                2. 保持总结的专业性和简洁性
                3. 只优化已有内容，不添加新信息
                4. 如果需要结合会议资料，请参考以下内容：
                --------------------
                {meeting_materials}
                --------------------
                请按照以下方式处理并返回总结：
                --------------------
                处理引擎
                1. **智能清洗**：
                   - 过滤重复语句（置信度>90%）
                   - 自动分段（按议题切分）
                2. **内容萃取**：
                   - 提取3-5个核心决策（带时间戳）
                   - 构建RACI责任矩阵（负责人/执行人/知会方）
                   - 生成甘特图框架（截止日期自动关联）
                3. **风险扫描**：
                   - 识别矛盾表述（如预算分歧）
                   - 标注未决议题（置信度<70%内容）
                   - 生成风险缓解建议（基于历史案例库）
                                
                输出模板
                # 2025Q3产品战略会纪要
                **时间**：2025-04-03 00:00:00
                **地点**：总部A栋会议室
                **核心成果**
                1. 确定新产品X上市计划（9月1日）
                2. 追加AI研发预算500万（技术部负责）
                **行动清单**：
                | 任务 | 责任人 | 节点 | 交付物 | 
                |------|--------|------|--------|
                | 用户调研 | 张经理 | 4/20 | 500份样本报告 |
                | 原型测试 | 李总监 | 5/15 | 测试验收单 |
                **遗留问题**：
                - 供应链备选方案待确认
                --------------------
                """.replace("{meeting_content}", meetingContent)
                .replace("{user_feedback}", feedback)
                .replace("{meeting_materials}", meetingMaterials)
                .replace("meeting_title", meeting.getTitle())
                .replace("{meeting_start_time}", meeting.getStartTime());

        return chatClient.prompt()
                .advisors(
                        new MessageChatMemoryAdvisor(inMemoryChatMemory)
                )
                // 使用原始会议内容作为对话起点
                .user(promptWithContext)
                .stream()
                .content()
                .map(chatResponse -> ServerSentEvent.builder(chatResponse)
                        .event("message")
                        .build())
                .concatWithValues(
                        ServerSentEvent.builder("event: end")
                                .event("message")
                                .build()
                );
    }

    @CrossOrigin(
            origins = "*",
            allowedHeaders = "*",
            exposedHeaders = "Content-Type",
            methods = {RequestMethod.POST}
    )
    @PostMapping("embedding")
    public Result embedding(
            @RequestParam("file") MultipartFile file,
            @RequestParam("meetingId") String meetingId
    ) throws IOException {
        if (!file.isEmpty()) {
            return Result.error("文件为空");
        }
        // 从IO流中读取文件
        TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(new InputStreamResource(file.getInputStream()));
        // 将文本内容划分成更小的块
        List<Document> splitDocuments = new TokenTextSplitter()
                .apply(tikaDocumentReader.read());
        // 为每个文档块附加 meeting_id 元数据
        splitDocuments = splitDocuments.stream()
                .map(doc -> {
                    Map<String, Object> metadata = new HashMap<>(doc.getMetadata());
                    metadata.put("meeting_id", meetingId);
                    return new Document(doc.getContent(), metadata);
                })
                .collect(Collectors.toList());
        // 存入向量数据库，这个过程会自动调用embeddingModel,将文本变成向量再存入。
        vectorStore.add(splitDocuments);
        return Result.success();
    }

    @GetMapping("query")
    public List<Document> query(@RequestParam String query) {
        return vectorStore.similaritySearch(query);
    }


    @GetMapping("/resolution")
    public Result resolution(@RequestParam(value = "meeting_id") String meetingId, @RequestParam(value = "text") String text) {
        if (text==null || text.isEmpty() || meetingId==null || meetingId.isEmpty()) return Result.error("参数有空");

        String prompt = """
                # 输入文本
                {text}
                
                # 处理规范
                1. 对话流分析：通过上下文语义分析、称谓变化、话轮转换特征等语言学要素，精确识别说话人交替节点
                2. 内容完整性：保持原始文本的完整性与连续性，禁止添加/删减/改写任何字符（包括标点符号）
                3. 格式标准化：
                   a. 每个独立话轮独占段落
                   b. 仅当检测到说话人变更时执行换行操作
                   c. 同一说话人的连续发言保持段落连续性
                4. 异常处理：
                   a. 对未明确说话人但存在语义转折的段落，采用相邻语义对比分析进行判断
                   b. 保留原始文本中的非对话内容（如有）为独立段落
                
                # 输出示例
                输入：今天天气不错是吧？确实比上周强多了。您觉得下午需要带伞吗？气象台说傍晚有阵雨。
                输出：
                今天天气不错是吧？
                确实比上周强多了。您觉得下午需要带伞吗？
                气象台说傍晚有阵雨。
                """.replace("{text}", text);

        String response = chatClient.prompt()
//                .advisors(
//                        new MessageChatMemoryAdvisor(inMemoryChatMemory)
//                )
                // 使用原始会议内容作为对话起点
                .user(prompt)
                .call()
                .content();

        List<String> lines = response.lines().toList();
        List<AiResponseVo> aiResponseVoList = new ArrayList<>();
        for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
            String line = lines.get(i);
            AiResponseVo aiResponseVo = new AiResponseVo();
            aiResponseVo.setSpeaker(String.valueOf(i));
            aiResponseVo.setText(line);
            aiResponseVoList.add(aiResponseVo);
        }

        return Result.success(aiResponseVoList);
    }
}

